package models

import (
	"errors"
	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/model"
	"gorm.io/datatypes"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models/query"
)

type SysDev struct {
	Id           string         `json:"id" gorm:"primaryKey;type:varchar(255);comment:id"`
	Sn           string         `json:"sn" gorm:"type:varchar(255);comment:设备序列号"`
	Name         string         `json:"name" gorm:"type:varchar(255);comment:设备名称"`
	DeptId       string         `json:"deptId" gorm:"type:varchar(255);comment:分组id"`
	OnlineStatus string         `json:"onlineStatus" gorm:"type:varchar(255);comment:在线状态"`
	Type         string         `json:"type" gorm:"type:varchar(255);comment:类型"`
	ProductModel string         `json:"productModel" gorm:"type:varchar(255);comment:产品编码"`
	GatewaySn    string         `json:"gatewaySn" gorm:"type:varchar(255);comment:网关SN"`
	AlarmStatus  bool           `json:"alarmStatus" gorm:"type:bool;comment:是否告警"`
	Location     datatypes.JSON `json:"location" gorm:"type:jsonb;comment:地址经纬度"`
	View         datatypes.JSON `json:"view" gorm:"type:jsonb;comment:个性页"`

	CreateTime   *cDto.LocalTime `json:"createTime" gorm:"type:timestamptz(6);comment:创建时间"`
	UpdateTime   *cDto.LocalTime `json:"updateTime" gorm:"type:timestamptz(6);comment:更新时间"`
	LastLiveTime *cDto.LocalTime `json:"lastLiveTime" gorm:"type:timestamptz(6);comment:末次交互时间"`
	RegisterTime *cDto.LocalTime `json:"registerTime" gorm:"type:timestamptz(6);comment:注册时间"`
	LogoutTime   *cDto.LocalTime `json:"logoutTime" gorm:"type:timestamptz(6);comment:登出时间"`

	model.Model

	DeptName string                 `json:"deptName" gorm:"-"`
	SvcInfo  map[string]interface{} `json:"svcInfo" gorm:"-"`
}

func (SysDev) TableName() string {
	return "sys_dev"
}

func (e *SysDev) Generate() interface{} {
	o := *e
	return &o
}

func (e *SysDev) GetId() interface{} {
	return e.Id
}

func (e *SysDev) FindDeviceList(req *query.SysDevQuery) (err error, result interface{}) {
	list := make([]SysDev, 0)
	var count int64
	if req.PageIndex > 0 && req.PageSize > 0 {
		err = e.FindPage(&list, &count,
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.OrderDest("create_time", true, req.OrderBy),
			cDto.Paginate(req.GetPageSize(), req.GetPageIndex()),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			if len(list[i].DeptId) > 0 {
				if deptName, ok := groupIdName[list[i].DeptId]; ok {
					list[i].DeptName = deptName
				}
			}
		}
		result = &cDto.Page{
			Records:   list,
			Total:     count,
			PageSize:  req.GetPageSize(),
			PageIndex: req.GetPageIndex(),
			Pages:     (count + int64(req.GetPageSize()) - 1) / int64(req.GetPageSize()),
			Other:     nil,
		}
	} else {
		err = e.FindList(&list,
			cDto.MakeCondition(req.GetNeedSearch()),
			cDto.OrderDest("create_time", true, req.OrderBy),
		)
		groupIdName, _ := utils.GroupIdName(e.Orm)
		for i := range list {
			if len(list[i].DeptId) > 0 {
				if deptName, ok := groupIdName[list[i].DeptId]; ok {
					list[i].DeptName = deptName
				}
			}
		}
		result = list
	}
	if req.Type == enums.DevTypeOfSoftGateway {
		if err = fillDeviceBizField(e.Orm, &list); err != nil {
			return
		}
	}
	return
}

func (e *SysDev) FindDevice(req *query.SysDevQuery) (err error, one SysDev) {
	err = e.FindOne(&one, cDto.MakeCondition(req.GetNeedSearch()))
	if len(one.Id) > 0 {
		if len(one.DeptId) > 0 {
			groupIdName, _ := utils.GroupIdName(e.Orm, []string{one.DeptId})
			if deptName, ok := groupIdName[one.DeptId]; ok {
				one.DeptName = deptName
			}
		}
		if req.Type == enums.DevTypeOfSoftGateway {
			//svc := &SysSvc{}
			//svc.Model.Orm = e.Orm
			//if err = svc.GetByInstanceId(one.Sn); err != nil {
			//	if !errors.Is(err, gorm.ErrRecordNotFound) {
			//		return
			//	}
			//	err = nil
			//}
			//if len(svc.Id) > 0 {
			//	one.SvcInfo = utils.DataToMap(svc)
			//}
		}
	}
	return
}

func (e *SysDev) AddDevice(addDev *SysDev, fromLoginInit ...bool) (err error) {
	if len(fromLoginInit) == 0 || !fromLoginInit[0] {
		tmpDev := SysDev{}
		cond := cDto.MakeCondition(query.SysDevQuery{IdSn: addDev.Sn})
		if err = e.FindOne(&tmpDev, cond); err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return
		}
		if len(tmpDev.Id) > 0 {
			// sn存在
			if tmpDev.DeptId != constant.AnonymousGroupId {
				err = errors.New("无法添加重复的设备SN")
				return
			}
			// 是陌生设备，则先删后加(不更新，为了逻辑走全)
			if err = e.Delete(&tmpDev, cond); err != nil {
				return
			}
			// 保留陌生设备之前的部分状态
			addDev.OnlineStatus = tmpDev.OnlineStatus
			addDev.LastLiveTime = tmpDev.LastLiveTime
		}
	}
	err = e.Add(addDev)
	return
}

func (e *SysDev) UpdateOnlineStatus(devSn, to, now, status string) error {
	modMap := map[string]interface{}{
		"online_status": status,
	}
	devQuery := query.SysDevQuery{}
	if status == enums.Online {
		// 上线
		if len(to) > 0 && to != devSn {
			// 设备
			devQuery.SnIn = []string{devSn, to}
		} else {
			// 网关
			devQuery.Sn = devSn
		}
		// 末次心跳
		modMap["last_live_time"] = *utils.FormatStrTimePoint(now)
	} else if status == enums.Offline {
		// 下线
		if len(to) > 0 && to != devSn {
			// 设备
			devQuery.Sn = to
		} else {
			// 网关
			devQuery.GateOrSn = devSn
		}
		// 在线的才更新下线
		devQuery.OnlineStatus = enums.Online
		// 登出时间
		modMap["logout_time"] = *utils.FormatStrTimePoint(now)
	} else {
		return nil
	}
	return e.ModMap(e, modMap, cDto.MakeCondition(devQuery))
}

func fillDeviceBizField(tx *gorm.DB, list *[]SysDev) error {
	//if len(*list) == 0 {
	//	return nil
	//}
	//snList := make([]string, 0)
	//for _, v := range *list {
	//	snList = append(snList, v.Sn)
	//}
	//svc := &SysSvc{}
	//svc.Model.Orm = tx
	//svcList, err := svc.FindSvcList(&query.SysSvcQuery{InstanceIdIn: snList})
	//if err != nil {
	//	return err
	//}
	//svcMap := make(map[string]SysSvc)
	//for i, v := range svcList {
	//	svcMap[v.InstanceId] = svcList[i]
	//}
	//for i, v := range *list {
	//	dev := &(*list)[i]
	//	if serve, ok := svcMap[v.Sn]; ok {
	//		dev.SvcInfo = utils.DataToMap(serve)
	//	}
	//}
	return nil
}

func (e *SysDev) ExistDevice(idSn string) (bool, error) {
	var count int64
	if err := e.FindCount(e, &count, cDto.MakeCondition(query.SysDevQuery{IdSn: idSn})); err != nil {
		return false, err
	}
	return count > 0, nil
}

func (e *SysDev) FindDeviceBySn(sn string) (dev SysDev, err error) {
	if err = e.FindOne(&dev, cDto.MakeCondition(query.SysDevQuery{Sn: sn})); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = nil
			return
		}
	}
	return
}

func (e *SysDev) RemoveDevBySn(sn string) (err error) {
	if err = e.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		e.Orm = tx
		devSchemaModel := &SysDevSchema{}
		devConfigModel := &SysDevConfig{}
		devSensorModel := &SysDevSensor{}
		devSystemModel := &SysDevSystem{}
		variableModel := &SysDevVariable{}
		devSchemaModel.Orm = tx
		devConfigModel.Orm = tx
		devSensorModel.Orm = tx
		devSystemModel.Orm = tx
		variableModel.Orm = tx
		// 设备基本信息
		if err = e.Delete(e, cDto.MakeCondition(query.SysDevQuery{Sn: sn})); err != nil {
			return err
		}
		// 设备schema
		if err = devSchemaModel.Delete(devSchemaModel, cDto.MakeCondition(query.SysDevSchemaQuery{Sn: sn})); err != nil {
			return err
		}
		// 设备profile.config
		if err = devConfigModel.Delete(devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{Sn: sn})); err != nil {
			return err
		}
		// 设备profile.sensor
		if err = devSensorModel.Delete(devSensorModel, cDto.MakeCondition(query.SysDevSensorQuery{Sn: sn})); err != nil {
			return err
		}
		// 设备profile.system
		if err = devSystemModel.Delete(devSystemModel, cDto.MakeCondition(query.SysDevSystemQuery{Sn: sn})); err != nil {
			return err
		}
		// 设备变量
		if err = variableModel.Delete(variableModel, cDto.MakeCondition(query.SysDevVariableQuery{Sn: sn})); err != nil {
			return err
		}
		// 删分组/设备缓存
		_ = env.Cache.HashDel(constant.DeviceDeptCache, sn)
		_ = env.Cache.HashDel(constant.DeviceEasyCache, sn)
		// 广播消息
		mqtt.MqttClient.Publish(constant.DeviceDeleteMsgTopic, utils.MapToBytes(map[string]interface{}{"sn": sn}))
		return nil
	}); err != nil {
		return
	}
	return
}

func (e *SysDev) GetDevSnList(cond ...func(db *gorm.DB) *gorm.DB) (snList []string, snDevMap map[string]SysDev) {
	snList = make([]string, 0)
	snDevMap = make(map[string]SysDev)
	devList := make([]SysDev, 0)
	if err := e.FindList(&devList, cond...); err != nil {
		return
	}
	for i, dev := range devList {
		snList = append(snList, dev.Sn)
		snDevMap[dev.Sn] = devList[i]
	}
	return
}

func (e *SysDev) FindStatCount(req *query.SysDevQuery) (err error, result interface{}) {
	list := make([]SysDev, 0)
	if err = e.FindList(&list, cDto.MakeCondition(req.GetNeedSearch())); err != nil {
		return
	}
	if req.ByProduct {
		pdModel := SysDevProduct{}
		pdModel.Orm = e.Orm
		pds := make([]SysDevProduct, 0)
		if err = pdModel.FindList(&pds, cDto.MakeCondition(query.SysProductQuery{DeptIdIn: req.DeptIdIn})); err != nil {
			return
		}
		byProduct := make(map[string]map[string]int)
		for _, v := range pds {
			byProduct[v.ProductModel] = make(map[string]int)
			byProduct[v.ProductModel][enums.Online] = 0
			byProduct[v.ProductModel][enums.Offline] = 0
		}
		arr := make([]map[string]interface{}, 0)
		for _, v := range list {
			if len(v.ProductModel) == 0 {
				continue
			}
			if _, ok := byProduct[v.ProductModel]; !ok {
				continue
			}
			if v.OnlineStatus == enums.Online {
				byProduct[v.ProductModel][enums.Online]++
			} else {
				byProduct[v.ProductModel][enums.Offline]++
			}
		}
		modelName := pdModel.GetModelName()
		for productModel, countMap := range byProduct {
			tmp := map[string]interface{}{
				"productModel": productModel,
				"productName":  modelName[productModel],
			}
			for k, v := range countMap {
				tmp[k] = v
			}
			arr = append(arr, tmp)
		}
		result = arr
	} else {
		statCount := make(map[string]int)
		statCount[enums.Online] = 0
		statCount[enums.Offline] = 0
		for _, v := range list {
			if v.OnlineStatus == enums.Online {
				statCount[enums.Online]++
			} else {
				statCount[enums.Offline]++
			}
		}
		result = statCount
	}
	return
}
